可分為多個CA
curl https://kube-apiserver:6443/api/v1/pods \
--key iroman.key --cert iroman.crt \
--cacert ca.crt
但是每次要取得 pod 資訊或者操作時都需要指定,因此可以將上述寫於 kube-config.yaml中,
這個在 EKS 中也就是透過 aws eks update-kubeconfig
取得連線,此時會在 ~/.kube/config儲存相關資訊。
以 config 的形式儲存於 ~/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ...
server: https://1A2B3D4C5E6F.gr7.us-east-1.eks.amazonaws.com
name: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
contexts:
- context:
cluster: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
user: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
name: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
current-context: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
kind: Config
preferences: {}
users:
- name: arn:aws:eks:us-east-1:123456789012:cluster/iroman-ut
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- --region
- us-east-1
- eks
- get-token
- --cluster-name
- iroman-ut
- --output
- json
command: aws
由上述可以看到在config中可以大致分為三個類別:(1)clusters (2)contexts (3)users
cluster及user分別是設定哪個cluster及user,context則是協助將cluster及user做mapping
如果需要取得相關連線資訊除了訪問檔案外可以透過
kubectl config get-contexts # 取得可操控 cluster 的列表
或者是透過以下指令查看細節
kubectl config view --raw # 查看完整的 config 細節
journalctl -u etcd.service -l
說明:加密與解密都使用同一把金鑰
說明:加密與解密是不同的金鑰,可以彼此作為加/解密的金鑰,可以分為Private Key以及 Public Key。
建立 ca
openssl genrsa -out ca.key 2048
ca 自簽憑證
openssl req -x509 -new -key ca.key -out ca.crt -days 3650 \
-subj "/CN=My CA/O=My Organization/C=TW"
建立 iroman.key
openssl genrsa -out iroman.key 2048
建立sign request
openssl req -new -key iroman.key -subj "/CN=iroman/O=developers" -out iroman.csr
CA 使用自己的憑證和私鑰簽署 CSR
openssl x509 -req -in iroman.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out iroman.crt -days 1000
Certificate Authority: ca.crt 憑證簽發機構(驗證身份並簽發憑證)
Certificate Signing Request: iroman.csr 憑證申請書(包含公鑰和身份資訊)
憑證檔案(包含公鑰和身份資訊): 通常是 .crt, .pem, .cer
私鑰檔案: 通常是 .key, -key.pem
流程:申請者生成 CSR → 提交給 CA → CA 驗證並簽發憑證
以 ssh 為例,當我們透過ssh-keygen
建立金鑰時,可以建立 Private Key(id_rsa) 以及 Public Key(id_rsa.pub),
創建完後將 public key 上傳至 Server(EC2) 中,一般來說ssh public key 會存於 EC2的~/.ssh/authorized_keys
。
使用時可以透過 ssh 連線 ssh -i {private_key} {user_name}@{server 位置}
連線,
當首次連線時會有以下的詢問
ED25519 key fingerprint is SHA256:ABCmrigLhkKz2bZC4MpOoliThtCKVnsRzPC1sA4TFKA.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
當輸入 yes 後,該EC2的公鑰資訊會存於本機端的 ~/.ssh/known_hosts中,每當連線時就會再次驗證該公鑰的正確性。
此外由於公鑰長度很長因此在連線時會將資訊轉為SHA256以便使用者(人)進行讀取
額外補充 ssh log 的存放位置
Debian Linux: /var/log/auth.log
CentOS : /var/log/secure.
Amazon Linux: /var/log/secure.
在 Amazon Linux 中如果要啟動 ssh log 會需要先建立syslog
dnf install rsyslog
systemctl enable rsyslog --now
TCP 交握 → TLS 交握 → 加密的 HTTP 通訊 → TLS/TCP 關閉
1. Client → Server: SYN
- 客戶端發起連線請求
2. Server → Client: SYN-ACK
- 伺服器確認並回應連線請求
3. Client → Server: ACK
- 客戶端確認,TCP 連線建立完成
4. Client Hello
- TLS 版本、隨機數、支援的密碼套件等
5. Server Hello
- 選擇的 TLS 版本、隨機數、密碼套件
6. Certificate
- 伺服器憑證鏈 (包含 Server 的 public key)
7. Server Key Exchange (可選)
8. Certificate Request (可選)
9. Server Hello Done
10. Certificate (可選)
- Client 憑證(雙向認證時)
11. Client Key Exchange
- Client 產生 pre-master secret
- 用 Server 的 public key 加密 pre-master secret
- 送給 Server
→ Server 用自己的 private key 解密取得 pre-master secret
12. Certificate Verify (可選)
- Client 用自己的 private key 簽名(證明擁有憑證)
13. Change Cipher Spec (Client)
14. Finished (Client, 加密)
15. Change Cipher Spec (Server)
16. Finished (Server, 加密)
17. HTTP Request/Response (全部加密)
18. TLS Close Notify
19. TCP 四向揮手 (FIN, ACK, FIN, ACK)
[FIN, ACK] 客戶端發起關閉 - "我要關閉連線了,並確認之前的資料"
[ACK] 伺服器確認 - "好的,我知道你要關閉了"
[FIN, ACK] 伺服器也要關閉 - "我也要關閉連線了"
[ACK] 客戶端最終確認 - "好的,連線正式關閉"
以下方式可以提供測試,在環境中建立一個http flask 並且透過 tcpdump 監聽,當使用者輸入密碼時會被抓取資訊
from flask import Flask, request, render_template_string
import json
from datetime import datetime
app = Flask(__name__)
# 簡單的 HTML 表單
HTML_FORM = '''
<!DOCTYPE html>
<html>
<head>
<title>HTTP 流量監聽實驗</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; }
form { background: #f4f4f4; padding: 20px; border-radius: 5px; }
input, button { margin: 10px 0; padding: 8px; width: 200px; }
button { background: #007cba; color: white; border: none; cursor: pointer; }
.result { margin-top: 20px; padding: 15px; background: #d4edda; border-radius: 5px; }
</style>
</head>
<body>
<h1>HTTP 流量監聽實驗</h1>
<p>這個頁面用於示範 HTTP 未加密通訊的安全風險</p>
<form method="POST" action="/submit">
<h3>請輸入一些「敏感」資料:</h3>
<br>用戶名稱: <input type="text" name="username" placeholder="例如: admin">
<br>密碼: <input type="password" name="password" placeholder="例如: secret123">
<br>API Token: <input type="text" name="api_token" placeholder="例如: abc123xyz789">
<br>信用卡號: <input type="text" name="credit_card" placeholder="例如: 1234-5678-9012-3456">
<br><button type="submit">提交資料</button>
</form>
{% if message %}
<div class="result">
<h3>結果:</h3>
<p>{{ message }}</p>
<p><strong>提示:檢查你的 tcpdump 終端,應該可以看到剛才提交的所有資料!</strong></p>
</div>
{% endif %}
</body>
</html>
'''
@app.route('/', methods=['GET', 'POST'])
def index():
message = None
return render_template_string(HTML_FORM, message=message)
@app.route('/submit', methods=['POST'])
def submit():
# 取得表單資料
data = {
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'username': request.form.get('username', ''),
'password': request.form.get('password', ''),
'api_token': request.form.get('api_token', ''),
'credit_card': request.form.get('credit_card', '')
}
# 儲存到檔案(模擬資料處理)
with open('/tmp/submitted_data.json', 'a') as f:
f.write(json.dumps(data, ensure_ascii=False) + '\n')
# 在終端顯示(方便觀察)
print(f"[{data['timestamp']}] 收到資料:")
print(f" 用戶名: {data['username']}")
print(f" 密碼: {data['password']}")
print(f" API Token: {data['api_token']}")
print(f" 信用卡: {data['credit_card']}")
print("-" * 50)
message = f"資料已成功提交!時間: {data['timestamp']}"
return render_template_string(HTML_FORM, message=message)
if __name__ == '__main__':
print("=" * 60)
print("HTTP 流量監聽實驗服務器")
print("=" * 60)
print("1. 在另一個終端執行以下命令開始監聽:")
print("""sudo tcpdump -i any -A -s 0 'port 5000' | grep -A 10 -B 10 "POST\|username\|password" """)
print("")
print("2. 然後在瀏覽器開啟: http://localhost:5000")
print("3. 填寫表單並提交,觀察 tcpdump 的輸出")
print("=" * 60)
# 綁定到 localhost:5000
app.run(host='127.0.0.1', port=5000, debug=True)